package dp.mobile.glwrapper;

import java.util.ArrayList;
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL10;

import dp.mobile.system.Utilities;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;

public class GLTextureLibrary {
	private static final String TAG = "Texture Library";
	
	public GLTextureLibrary() {
		mHealthBarImages 		= new ArrayList<GLTexture>();
		mTextures 				= new HashMap<String, GLTexture>();
		mTextureNameWorkspace 	= new int[1];
		//mCropWorkspace 			= new int[4];
		
		//sBitmapOptions.inJustDecodeBounds = true;
		mRangeImage 		= allocateTexture(Utilities.INTERFACE_FOLDER + "/circle.png", false);
		mHealthBarImage 	= allocateTexture(Utilities.INTERFACE_FOLDER + "/hp_bar.png", false);
		mProgressBarImage 	= allocateTexture(Utilities.INTERFACE_FOLDER + "/progress_bar.png", false);
		
		mHealthBarImages.add(mHealthBarImage);
		mHealthBarImages.add(allocateTexture(Utilities.INTERFACE_FOLDER + "/hp_bar2.png", false));
	}
	
	public GLTexture allocateTexture(String filePath, boolean needBound) {
		GLTexture texture = mTextures.get(filePath);
		int width 	= 0;
		int height 	= 0;
		
		if (needBound) {
			BitmapFactory.Options bound = new BitmapFactory.Options();
			bound.inJustDecodeBounds = true;
			BitmapFactory.decodeFile(filePath, bound);
			width 	= bound.outWidth;
			height 	= bound.outHeight;
		}
		
		Log.d(TAG, "Allocating " + filePath);
		if (texture == null)
			texture = addTexture(filePath, -1, width, height);
		
		return texture;
	}
	
    /** Loads a single texture into memory.  Does nothing if the texture is already loaded. */
    public GLTexture loadTexture(Context context, GL10 gl, String path) {
    	GLTexture texture = allocateTexture(path, false);
        texture = loadBitmap(context, gl, texture);
        return texture;
    }

    /** Loads all unloaded textures into OpenGL memory.  Already-loaded textures are ignored. */
    public void loadAll(Context context, GL10 gl) {
        for (GLTexture texture : mTextures.values()) {
            if (texture.Resource != null && texture.Loaded == false) {
                loadBitmap(context, gl, texture);
            }
        }
        mValidated = true;
    }
      
	// Flushes all textures from OpenGL memory
    public void deleteAll(GL10 gl) {
    	for (GLTexture texture : mTextures.values()) {
            if (texture.Resource != null && texture.Loaded) {
                assert texture.Name != -1;
                
                mTextureNameWorkspace[0] = texture.Name;
                texture.Name = -1;
                texture.Loaded = false;
                gl.glDeleteTextures(1, mTextureNameWorkspace, 0);
                
                int error = gl.glGetError();
                
                assert error == GL10.GL_NO_ERROR;
            }
        }
    }
    
    // flush specific texture from OpenGL memory
    public void deleteTexture(GL10 gl, String path) {
    	GLTexture texture = mTextures.get(path);
    	
    	if (texture != null && texture.Resource != null && texture.Loaded) {
            assert texture.Name != -1;
            
            mTextureNameWorkspace[0] = texture.Name;
            texture.Name = -1;
            texture.Loaded = false;
            gl.glDeleteTextures(1, mTextureNameWorkspace, 0);
            
            int error = gl.glGetError();
            
            assert error == GL10.GL_NO_ERROR;
        }
    }
	
	// Marks all textures as unloaded 
    public void invalidateAll() {
    	for (GLTexture texture : mTextures.values()) {
    		texture.Name = -1;
    		texture.Loaded = false;
    	}
    	mValidated = false;
    }
	
	// Loads a bitmap into OpenGL and sets up the common parameters for 2D texture maps. 
    protected GLTexture loadBitmap(Context context, GL10 gl, GLTexture texture) {
        assert gl != null;
        assert context != null;
        assert texture != null;
        if (texture.Loaded == false && texture.Resource != null) {
            gl.glGenTextures(1, mTextureNameWorkspace, 0);
            
            int error = gl.glGetError();
            
            assert error == GL10.GL_NO_ERROR;
            
            int textureName = mTextureNameWorkspace[0];
            
            gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
            
            error = gl.glGetError();
            
            assert error == GL10.GL_NO_ERROR;

            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

            gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);

            Bitmap bitmap = BitmapFactory.decodeFile(texture.Resource);
            
            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
            
            error = gl.glGetError();
           
            assert error == GL10.GL_NO_ERROR;

            /*mCropWorkspace[0] = 0;
            mCropWorkspace[1] = bitmap.getHeight();
            mCropWorkspace[2] = bitmap.getWidth();
            mCropWorkspace[3] = -bitmap.getHeight();

            ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0);
             */
            texture.Name 	= textureName;
            texture.Width 	= bitmap.getWidth();
            texture.Height 	= bitmap.getHeight();

            bitmap.recycle();
            
            error = gl.glGetError();
            
            assert error == GL10.GL_NO_ERROR;
            
            texture.Loaded = true;
            
            Log.d(TAG, "Texture loaded " + texture.Name + " " + texture.Resource);
        }

        return texture;
    }
	
	public boolean isTextureLoaded(String path) {
        return getTextureByResource(path) != null;
    }
	
	public void fakeInvalidate() {
		mValidated = false;
	}
	
	public boolean isValidated() {
		return mValidated;
	}
	
	public GLTexture getTextureByResource(String path) {
        return mTextures.get(path);
    }
	
	GLTexture getRangeImage() {
		return mRangeImage;
	}
	
	GLTexture getHealthBarImage() {
		return mHealthBarImage;
	}
	
	GLTexture getHealthBarImage(int index) {
		if (mHealthBarImages.size() > index)
			return mHealthBarImages.get(index);
		else
			return null;
	}
	
	GLTexture getProgressBarImage() {
		return mProgressBarImage;
	}
	
	protected GLTexture addTexture(String id, int name, int width, int height) {
		GLTexture texture 	= new GLTexture();
		texture.Resource 	= id;
		texture.Name 		= name;
		texture.Width 		= width;
		texture.Height 		= height;
		mTextures.put(id, texture);
   
        return texture;
	}
	
	public void removeAll() {
		mTextures.clear();
		System.gc();
	}

	// universal game image
	private HashMap<String, GLTexture> mTextures;
	private GLTexture mRangeImage;
	private GLTexture mHealthBarImage;
	private GLTexture mProgressBarImage;
	
	private ArrayList<GLTexture> mHealthBarImages;
	private int[] mTextureNameWorkspace;
	private boolean mValidated;
	//private int[] mCropWorkspace;
	
	//public static BitmapFactory.Options sBitmapOptions  = new BitmapFactory.Options();
}
